
#pragma once

#include <ttg/threads.h>
#include <ttg/pipeline.h>
#include "GL/glut.h"

using namespace ttg::pipeline;

class GlutThread :public ttg::threads::Thread
{
	private:
		static GlutThread *ref;
	private:
		bool is_inited;
		std::vector<ttg::structures::InvokableObject *> actions;
		ttg::threads::SlowEvent main_thread;
		ttg::threads::SlowEvent glut_thread;
	private:
		inline GlutThread()
			:ttg::threads::Thread(false)
		{ is_inited = false; }
		static void displayFunc();
		static void idleFunc()
		{ glutPostRedisplay(); }
		virtual void work();
	public:
		void performActions(std::vector<ttg::structures::InvokableObject *> &actions);
		void init();
		static inline GlutThread *getRef()
		{ return ref; }
};

template <class T>
class glutVisualizer :public ContextedNode<T, T>
{
	private:
		std::vector<ttg::structures::InvokableObject *> actions;
	private:
		static void onBeginWrapper(glutVisualizer<T> *object, void *v)
		{ object->onBegin(); }
		virtual void onBegin(ContextID ID)
		{
			if (getIContext().getRootContextID() != ID)
				throw ttg::Error(ttg::Error::FATAL, "glutVisualizer", "onBegin",
								 "glutVisualizer cannot be used without root context");
			actions.push_back(ttg::structures::make_invokable_object(
					ttg::structures::make_delegate(this, onBeginWrapper),
					(void *)NULL));
		}
		virtual void process(ContextID ID, T data)
		{
			if (getIContext().getRootContextID() != ID)
				throw ttg::Error(ttg::Error::FATAL, "glutVisualizer", "onBegin",
								 "glutVisualizer cannot be used without root context");
			actions.push_back(ttg::structures::make_invokable_object(
					ttg::structures::make_delegate(this, &glutVisualizer<T>::process),
					data));
		}
		static void onEndWrapper(glutVisualizer<T> *object, void *v)
		{ object->onEnd(); }
		virtual void onEnd(ContextID ID)
		{
			if (getIContext().getRootContextID() != ID)
				throw ttg::Error(ttg::Error::FATAL, "glutVisualizer", "onBegin",
								 "glutVisualizer cannot be used without root context");
			actions.push_back(ttg::structures::make_invokable_object(
					ttg::structures::make_delegate(this, onEndWrapper),
					(void *)NULL));
			GlutThread::getRef()->performActions(actions);
			actions.clear();
		}
	public:
		virtual void onBegin()
		{ /*nothing*/ }
		virtual void process(T data) = 0;
		virtual void onEnd()
		{ /*nothing*/ }
		inline glutVisualizer()
			:ContextedNode("glutVisualizer", true)
		{
			GlutThread::getRef()->init();
		}
};
